#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>

using namespace std;

/**
 * 创建Shader
 * 加载Shader源码，并进行编译
 * 返回Shader的索引
 */
static unsigned int CompileShader(unsigned int type, const std::string& source)
{
    unsigned int shader = glCreateShader(type);
    const char* src = source.c_str();
    glShaderSource(shader, 1, &src, nullptr);
    glCompileShader(shader);

    int result;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
    if (result == GL_FALSE) {
        int length;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
        char* message = (char *) alloca(length * sizeof(char));
        glGetShaderInfoLog(shader, length, &length, message);
        cout << "Failed to compile " << ((type == GL_VERTEX_SHADER) ? "vertex" : "fragment") << " shader: " << message << endl;
    }

    return shader;
}

/**
 * 创建ShaderProgram
 * 调用CompileShader编译Shader源码
 * 将编译后的Shader，添加到Program中
 * 并链接Program，生成可执行的Shader程序
 * 删除编译后的Shader
 * 返回Program索引
 */
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{
    unsigned int program = glCreateProgram();
    unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
    unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);

    glAttachShader(program, vs);
    glAttachShader(program, fs);
    glLinkProgram(program);
    glValidateProgram(program);

    glDeleteShader(vs);
    glDeleteShader(fs);

    return program;
}

int main(void)
{
    cout << "Hello World!" << endl;

    GLFWwindow* window;

    if (!glfwInit()) {
        cout << "glfwInit error!" << endl;
        return -1;
    }

    // glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 
    // glfwWindowHint(GLFW_VERSION_MINOR, 6); 
    // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 
    // glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); 

    // bool isFullScreen = false;
    // GLFWmonitor* pMonitor = isFullScreen ? glfwGetPrimaryMonitor() : NULL;

    window = glfwCreateWindow(640, 480, "Hello World", nullptr, nullptr);
    if (!window) {
        cout << "glfwCreateWindow error!" << endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);

    if (glewInit() != GLEW_OK) {
        cout << "glewinit error!" << endl;
    }

    cout << "GL_VERSION: " << glGetString(GL_VERSION) << endl;

    // 顶点信息，只包含二维位置坐标信息
    float positions[6] = {
        -0.5f, -0.5f,
         0.0f,  0.5f,
         0.5f, -0.5f
    };

    unsigned int buffer;
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float), positions, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);

    // 顶点着色器代码
    std:: string vertexShader = 
        "#version 330 core\n"
        "\n"
        "layout(location = 0) in vec2 position;\n"
        "\n"
        "void main() {\n"
        "   gl_Position = vec4(position.xy, 1.0f, 1.0f);\n"
        "}\n";
    // 片元着色器代码
    std:: string fragmentShader = 
        "#version 330 core\n"
        "\n"
        "out vec4 color;\n"
        "\n"
        "void main() {\n"
        "   color = vec4(1.0, 1.0, 1.0, 1.0);\n"
        "}\n";
    unsigned int shader = CreateShader(vertexShader, fragmentShader);
    // 使用这个程序
    glUseProgram(shader);
    // 顶点着色器代码
    std:: string vertexShader2 = 
        "#version 330 core\n"
        "\n"
        "layout(location = 0) in vec2 position;\n"
        "\n"
        "void main() {\n"
        "   gl_Position = vec4(position.xy * vec2(0.1, 0.1), 1.0f, 1.0f);\n"
        "}\n";
    // 片元着色器代码
    std:: string fragmentShader2 = 
        "#version 330 core\n"
        "\n"
        "out vec4 color;\n"
        "\n"
        "void main() {\n"
        "   color = vec4(1.0, 0.0, 0.0, 0.5);\n"
        "}\n";
    unsigned int shader2 = CreateShader(vertexShader2, fragmentShader2);
    // 使用这个程序
    glUseProgram(shader2);

    while(!glfwWindowShouldClose(window)) {
        glClear(GL_COLOR_BUFFER_BIT);
        glUseProgram(shader);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glUseProgram(shader2);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glfwSwapBuffers(window);

        glfwPollEvents();
    }

    glDeleteShader(shader);

    glfwDestroyWindow(window);
    glfwTerminate();

    cout << "main exit !" << endl;
    return 0;
}
